package cn.xawl.gulimall.product.service.impl;

import cn.xawl.gulimall.product.entity.CategoryBrandRelationEntity;
import cn.xawl.gulimall.product.service.CategoryBrandRelationService;
import cn.xawl.gulimall.product.vo.Catalog2Vo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.xawl.common.utils.PageUtils;
import cn.xawl.common.utils.Query;

import cn.xawl.gulimall.product.dao.CategoryDao;
import cn.xawl.gulimall.product.entity.CategoryEntity;
import cn.xawl.gulimall.product.service.CategoryService;
import org.springframework.util.StringUtils;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    private CategoryBrandRelationService categoryBrandRelationService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<>()
        );
        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> queryTreeList() {
        List<CategoryEntity> categoryEntityList = super.baseMapper.selectList(null);
        List<CategoryEntity> parentList = categoryEntityList.stream().filter(categoryEntity -> {
            return categoryEntity.getParentCid() == 0;   //过滤获取一级分类（parentCid为0的分类）
        }).collect(Collectors.toList());

        //根据每个其他分类的parentCid和一级分类的分类层级（level）获取子分类
        return parentList.stream()
                .peek(parent -> parent.setChildren(getChildrenList(categoryEntityList, parent)))
                .sorted(Comparator.comparingInt(CategoryEntity::getSort)).collect(Collectors.toList());
    }


    /**
     * 递归获取所有的子分类
     */
    private List<CategoryEntity> getChildrenList(List<CategoryEntity> allCategories, CategoryEntity parent) {
        return allCategories.stream().filter((allCategory) -> {
            return allCategory.getParentCid().equals(parent.getCatId());  //遍历所有分类，如果当前分类的parentId等于一级分类的catId，则说明当前分类为该一级分类下的分类
        }).peek((secondCategory) -> {
            //继续递归查找二级分类分子分类
            secondCategory.setChildren(getChildrenList(allCategories, secondCategory));
        }).sorted(Comparator.comparingInt(CategoryEntity::getSort)).collect(Collectors.toList());
    }

    @CacheEvict(value = "category", allEntries = true)
    @Override
    public void removeCategoryByIds(List<Long> asList) {
        //TODO 首先判断当前category是否内其他地方引用
        super.baseMapper.deleteBatchIds(asList); //这里使用逻辑删除，只修改某个字段显示或者不显示
    }

    @CacheEvict(value = "category", allEntries = true)
    @Override
    public void updateByIdCascade(CategoryEntity category) {
        //先更新自己的信息
        this.updateById(category);
        //再更新 关联表的信息
        this.categoryBrandRelationService.update(new UpdateWrapper<CategoryBrandRelationEntity>().set("catalog_name", category.getName()).eq("catalog_id", category.getCatId()));
    }

    @Cacheable(value = "category", key = "#root.method.name", sync = true)
    @Override
    public List<CategoryEntity> firstLevelCategories() {

        return this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));

    }

    @Override
    @Cacheable(value = "category", key = "#root.method.name")
    public Map<String, List<Catalog2Vo>> buildCatalog2Vo() {
        //redis中key值一般为String，value也一般为json字符串，所以直接使用StringRedisTemplate
        //JSON好处：跨平台性好，其他语言也可以将json字符串转换为需要的对象
        return redissonLock();
        //缓存中取出后，将json字符串转换为当时存储时的对象
    }

    //分布式锁
    private Map<String, List<Catalog2Vo>> redisLock() {
        String token = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", token, 300, TimeUnit.SECONDS);
        assert lock != null;
        if (lock) {
            System.out.println("获取到redis锁" + redisTemplate.opsForValue().get(" "));
            //获取到锁，执行查询操作
            //设置锁的过期时间，防止锁释放前出现异常导致无法释放锁（并且获取锁和设置过期时间必须是原子操作）
            Map<String, List<Catalog2Vo>> categoryFromDB = childCategories();
            //为redis服务器发送一个脚本，只有当锁的key和设置的value相同时才可以删除锁。
            String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
            redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList("lock"), token);
            System.out.println("释放了redis锁");
            return categoryFromDB;
        } else {
            try {
                //每隔100ms就去重新获取锁
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return redisLock(); //如果没有获取到分布式锁，就循环获取
        }


    }

    //redisson提供的分布式锁
    private Map<String, List<Catalog2Vo>> redissonLock() {
        RLock lock = redissonClient.getLock("category-lock");
        lock.lock();
        Map<String, List<Catalog2Vo>> categoryFromDB = null;
        try {
            //获取到锁，执行查询操作
            categoryFromDB = childCategories();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return categoryFromDB;
    }

    private Map<String, List<Catalog2Vo>> localLock() {
        //将 确认缓存中有无数据、查数据库、放入缓存 三个操作同时加锁。
        synchronized (this) {
            //确认缓存中有无数据
            return childCategories();
        }

    }


    public Map<String, List<Catalog2Vo>> childCategories() {
        //查询出一级分类
        List<CategoryEntity> firstLevelCategories = firstLevelCategories();
        //TODO 此处会循环的查询数据库，导致性能下降
        //解决方案1：一次性查出所有分类，保存在List中，再根据parent_cid获取不同级别的分类
        //解决方案2：将所有分类放入缓存
        List<CategoryEntity> allCategories = this.list(null); //查出所有分类

        //查询出每一个一级分类下的二级分类
        //查出后将该对象转换为json字符串存入缓存中
        return firstLevelCategories.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), first -> {
//            List<CategoryEntity> category2Entities = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", first.getCatId()));
            List<CategoryEntity> category2Entities = getDiffLevelCategory(allCategories, first.getCatId());
            return category2Entities.stream().map(two -> {
                Catalog2Vo catalog2Vo = new Catalog2Vo();
                catalog2Vo.setCatalog1Id(first.getCatId());
                catalog2Vo.setId(two.getCatId());
                catalog2Vo.setName(two.getName());

//                List<CategoryEntity> category3Entities = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", two.getCatId()));
                List<CategoryEntity> category3Entities = getDiffLevelCategory(allCategories, two.getCatId());
                List<Catalog2Vo.Catalog3Vo> catalog3VoList = category3Entities.stream().map(third -> {
                    Catalog2Vo.Catalog3Vo catalog3Vo = new Catalog2Vo.Catalog3Vo();
                    catalog3Vo.setCatalog2Id(third.getParentCid());
                    catalog3Vo.setId(third.getCatId());
                    catalog3Vo.setName(third.getName());
                    return catalog3Vo;
                }).collect(Collectors.toList());

                catalog2Vo.setCatalog3List(catalog3VoList);
                return catalog2Vo;
            }).collect(Collectors.toList());
        }));
    }

//    private Map<String, List<Catalog2Vo>> getCategoryFromDB() {
//        String categoryJson = redisTemplate.opsForValue().get("categoryJson");
//        if (!StringUtils.isEmpty(categoryJson)) {
//            //不为空直接返回缓存数据
//            return JSON.parseObject(categoryJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {
//            });
//        }
//
//        //查询出一级分类
//        List<CategoryEntity> firstLevelCategories = firstLevelCategories();
//
//        //TODO 此处会循环的查询数据库，导致性能下降
//        //解决方案1：一次性查出所有分类，保存在List中，再根据parent_cid获取不同级别的分类
//        //解决方案2：将所有分类放入缓存
//        List<CategoryEntity> allCategories = this.list(null); //查出所有分类
//
//        //查询出每一个一级分类下的二级分类
//        Map<String, List<Catalog2Vo>> result = firstLevelCategories.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), first -> {
////            List<CategoryEntity> category2Entities = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", first.getCatId()));
//            List<CategoryEntity> category2Entities = getDiffLevelCategory(allCategories, first.getCatId());
//            return category2Entities.stream().map(two -> {
//                Catalog2Vo catalog2Vo = new Catalog2Vo();
//                catalog2Vo.setCatalog1Id(first.getCatId());
//                catalog2Vo.setId(two.getCatId());
//                catalog2Vo.setName(two.getName());
//
////                List<CategoryEntity> category3Entities = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", two.getCatId()));
//                List<CategoryEntity> category3Entities = getDiffLevelCategory(allCategories, two.getCatId());
//                List<Catalog2Vo.Catalog3Vo> catalog3VoList = category3Entities.stream().map(third -> {
//                    Catalog2Vo.Catalog3Vo catalog3Vo = new Catalog2Vo.Catalog3Vo();
//                    catalog3Vo.setCatalog2Id(third.getParentCid());
//                    catalog3Vo.setId(third.getCatId());
//                    catalog3Vo.setName(third.getName());
//                    return catalog3Vo;
//                }).collect(Collectors.toList());
//
//                catalog2Vo.setCatalog3List(catalog3VoList);
//                return catalog2Vo;
//            }).collect(Collectors.toList());
//        }));
//        //查出后将该对象转换为json字符串存入缓存中
//        String categoryJsonString = JSON.toJSONString(result);
//        redisTemplate.opsForValue().set("categoryJson", categoryJsonString, 1, TimeUnit.DAYS);
//        return result;
//    }

    //根据所有分类以及父分类id获取不同级别分类
    private List<CategoryEntity> getDiffLevelCategory(List<CategoryEntity> all, Long parentCid) {
        return all.stream().filter(item -> item.getParentCid().equals(parentCid)).collect(Collectors.toList());
    }


}